package com.zl.quickdoc.resolver;

import java.io.File;
import java.io.FileInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.zl.quickdoc.utils.ParserJavaUtils;

/**
 * java源码缓存
 */
public class JavaSourceFileFactory {

    private static final JavaSourceFileFactory instance = new JavaSourceFileFactory();
    private static final JavaParser javaParser = new JavaParser();
    
    private final Map<String, CompilationUnit> javaPath = new LinkedHashMap<>();
    private final Map<String, List<String>> extendMap = new LinkedHashMap<>();
    private final Map<String, List<String>> implementedMap = new LinkedHashMap<>();
    
    private ClassLoader classLoader=null;
    
    public static JavaSourceFileFactory getInstance() {
        return instance;
    }

    public Map<String, CompilationUnit> buildJava(String path) {
        if(StringUtils.isBlank(path)) {
            System.out.println("【解析java源码】源码路径不能为空");
            return new HashMap<>(0);
        }
        List<File> files = new ArrayList<>();
        String[] paths= path.split(" *, *");
        for (String px : paths) {
            File  dir = new File(px);
            if (!dir.exists()) {
                System.out.println("【解析java源码】源码路径不存在>>"+ dir.getAbsolutePath());
                continue;
            }
            if (!dir.isDirectory()) {
                System.out.println("【解析java源码】源码路径不是目录>>"+ dir.getAbsolutePath());
                continue;
            }
            
            System.out.println("【解析java源码】开始解析>>"+ dir.getAbsolutePath());
            List<File> childrens = getJavaFiles(dir);
            if(childrens.isEmpty()) {
                continue;
            }
            files.addAll(childrens);
        }
        
        for (File file : files) {
            try (FileInputStream in = new FileInputStream(file)) {
                Optional<CompilationUnit> optional = javaParser.parse(in).getResult();
                if (!optional.isPresent()) {
                    continue;
                }
                CompilationUnit cu = optional.get();
                if (cu.getTypes().isEmpty()) {
                    continue;
                }
                TypeDeclaration<?> typeDeclaration = cu.getType(0);
                if(!typeDeclaration.isClassOrInterfaceDeclaration()) {//保留接口和类
                    continue;
                }
                String classname = ParserJavaUtils.getWholeClassName(cu, typeDeclaration.getNameAsString());
                javaPath.put(classname, cu);
                
                List<String> extendedClazz = ParserJavaUtils.getClassForExtend(cu);
                List<String> implementedClazz = ParserJavaUtils.getClassForImplemented(cu);
                if(extendedClazz!=null && !extendedClazz.isEmpty()){
                    extendMap.put(classname, extendedClazz);
                }
                if(implementedClazz!=null && !implementedClazz.isEmpty()){
                    implementedMap.put(classname, implementedClazz);
                }
                //解析父级
            } catch (Exception e) {
                System.out.println("【解析java源码】解析代码错误:"+ file+", "+ e.getMessage());
            }
        }
        
        return javaPath;
    }
    
    /**
     * 获取目录及子目录的Java文件
     */
    public List<File> getJavaFiles(File file) {
        
        if (!file.exists()) {
            return new ArrayList<>(0);
        }
        List<File> list = new ArrayList<>();
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null) {
                return list;
            }
            for (File f : files) {
                list.addAll(getJavaFiles(f));
            }
        }
        else if (file.isFile() && file.getName().lastIndexOf(".java") > 0) {
            list.add(file);
        }
        return list;
    }

    public ClassLoader buildClass(String path) throws MalformedURLException {
        if(classLoader != null){
            return classLoader;
        }
        if(StringUtils.isBlank(path)){
            return ClassLoader.getSystemClassLoader();
        }
        String[] paths = path.split(",");
        boolean flag = false;
        for(String x:paths){
            File f = new File(x);
            if(!f.exists()){
                System.out.println("【解析源码class】class路径不存在>>"+x);
            }
            if(!f.isDirectory()){
                System.out.println("【解析源码class】class路径必须是目录>>"+x);
                continue;
            }
            if(!ckeckClassFiles(f)) {
                System.out.println("【解析源码class】路径中不存在class文件>>"+x);
            }else {
                flag = true;
            }
        }
        if(!flag) {
            throw new RuntimeException("【解析源码class】未检测到任何class文件!");
        }
       
        List<URL> classUrls = new ArrayList<>();
        for(String x:paths){
            File f = new File(x);
            if(!f.exists()){
                continue;
            }
            if(!f.isDirectory()){
                continue;
            }
            System.out.println("【解析源码class】开始加载>>"+x);
            classUrls.add(f.toURI().toURL());
        }
        URL[] urls = classUrls.toArray(new URL[0]);
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        classLoader = new URLClassLoader(urls,systemClassLoader);
        return classLoader;
    }
    /**
     * 检验class文件
     */
    public boolean ckeckClassFiles(File file) {
        if (!file.exists()) {
            return false;
        }
        
        if (file.isFile()) {
            if (file.getName().lastIndexOf(".class") > 0) {
                return true;
            }
        }
        else if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    if(ckeckClassFiles(f)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    public CompilationUnit getJavaUnit(String classname) {
        return javaPath.get(classname);
    }
    public Map<String, CompilationUnit> getJavaUnit() {
        return javaPath;
    }
    public List<String> getExtendClassname(String classname) {
        return extendMap.get(classname);
    }
    public List<String> getImplementedClassname(String classname) {
        return implementedMap.get(classname);
    }
    
    public Class<?> getExistClass(String name) {
        try{
            Class<?> clz = classLoader.loadClass(name);
            if(clz.isPrimitive() || clz.isLocalClass() || clz.isInterface()
                    || clz.isArray() || clz.isEnum() || clz.isMemberClass()
                    || clz.isAnonymousClass() || clz.isSynthetic()) {
                return null;
            }
            return clz;
        }catch(ClassNotFoundException e){
            return null;
        }
    }
    
    public List<CompilationUnit> getSelfFatherClass(String classname) {
        List<CompilationUnit> list = new ArrayList<>();
        CompilationUnit self = javaPath.get(classname);
        if(self == null) {
            return list;
        }
        list.add(self);
        List<String> nowlist = extendMap.get(classname);
        if(nowlist != null && !nowlist.isEmpty()) {
           for(String x :nowlist) {
               List<CompilationUnit> list2 = getSelfFatherClass(x);
               if(!list2.isEmpty()) {
                   list.addAll(list2);
               }
           }
        }
        return list;
    }
    public List<CompilationUnit> getSelfFatherInterface(String classname) {
        List<CompilationUnit> list = new ArrayList<>();
        CompilationUnit self = javaPath.get(classname);
        if(self == null) {
            return list;
        }
        list.add(self);
        List<String> nowlist = implementedMap.get(classname);
        if(nowlist != null && !nowlist.isEmpty()) {
           for(String x :nowlist) {
               List<CompilationUnit> list2 = getSelfFatherInterface(x);
               if(!list2.isEmpty()) {
                   list.addAll(list2);
               }
           }
        }
        return list;
    }
}
